using System;
using System.Collections.Generic;
using System.Linq;

namespace Windy.OpenLink.ObjectPools
{
    public abstract class HierarchyObjectPool : ObjectPool, IObjectPool
    {
        #region Constructors

        protected HierarchyObjectPool(IEnumerable<KeyValuePair<string, IObjectPool>> subPools)
        {
            var dict = subPools as IDictionary<string, IObjectPool>;
            if (dict != null)
            {
                this.SubPools = dict;
            }
            else
            {
                this.SubPools = new Dictionary<string, IObjectPool>();
                this.SubPools.AddRange(subPools);
            }
        }

        #endregion Constructors

        #region Internal Members

        protected IDictionary<string, IObjectPool> SubPools;

        #endregion Internal Members

        #region Assistant Members

        protected IEnumerable<KeyValuePair<string, IObjectPool>> FindPossibleSubPoolInfos(string id, bool sort = false)
        {
            return
                sort ?
                from fo in SubPools
                where id.StartsWith(fo.Key)
                orderby fo.Key.Length descending
                select fo
                :
                from fo in SubPools
                where id.StartsWith(fo.Key)
                select fo;
        }

        protected IObjectPool FindMostSuitableSubPool(string id)
        {
            return FindPossibleSubPoolInfos(id, true).FirstOrDefault().Value ?? this;
        }

        protected IEnumerable<IObjectPool> FindPossiblePools(string id, bool sort = true)
        {
            return from fo in FindPossibleSubPoolInfos(id, sort)
                   select fo.Value;
        }

        #endregion Assistant Members

        #region IObjectPool Members

        public override bool TryGet(string id, out object value)
        {
            if (base.TryGet(id, out value))
                return true;

            foreach (var fo in FindPossiblePools(id))
            {
                if (fo.TryGet(id, out value))
                    return true;
            }

            return false;
        }

        public override int Count
        {
            get { return this.Count + SubPools.Values.Sum((pool) => pool.Count); }
        }

        public override void Set<T>(string id, T value)
        {
            FindMostSuitableSubPool(id).Set(id, value);
        }

        public override IEnumerable<string> Ids
        {
            get
            {
                var subKeys = from foSubPool in SubPools.Values
                              from foKey in foSubPool.Ids
                              select foKey;

                return this.Ids.Concat(subKeys);
            }
        }

        public override bool IsExist(string id)
        {
            return
                base.IsExist(id) ||
                SubPools.Values.Any((pool) => pool.IsExist(id));
        }

        public override bool Remove(string id)
        {
            return
                base.Remove(id) ||
                SubPools.Values.Any((pool) => pool.Remove(id));
        }

        public override bool RemoveTree(string id, bool includeRoot = true)
        {
            bool result = base.RemoveTree(id, includeRoot);
            foreach (var fo in FindPossiblePools(id, false))
                result |= fo.RemoveTree(id, includeRoot);

            return result;
        }

        #endregion IObjectPool Members
    }
}
